/*
 * =====================================================================================
 *
 *       Filename:  MoboThumbnail.c
 *
 *    Description: generate media thumbnail 
 *
 *        Version:  1.0
 *        Created:  10/16/2014 09:58:31
 *       Revision:  none
 *       Compiler:  
 *
 *         Author:  sunjun (), 
 *   Organization:  
 *
 * =====================================================================================
 */

#import "MoboThumbnail.h"
#import "libavformat/avformat.h"
#import "libswscale/swscale.h"

static MoboThumbnail *manager  = nil;


@interface Audioinfo()
//@property (readwrite,nonatomic,strong) NSString* Title;
//@property (readwrite,nonatomic,strong) NSString* Language;
//@property (readwrite,nonatomic,strong) NSString* CodecName;
//@property (readwrite,nonatomic) int sample_rate    ; //采样率
@end
@implementation Audioinfo
-(void) setaudio:(NSString *)title Language:(NSString*)language CodecName:(NSString*)codecname sample_rate:(int)sample_rate;
{
    Title =title;
    Language =language;
    CodecName=codecname;
    Sample_rate=sample_rate;
}
@end
@interface Subtitleinfo ()

@end
@implementation Subtitleinfo
-(void)setsubtitle:(NSString *)title Language:(NSString *)language CodecName:(NSString *)codecname sample_rate:(int)sample_rate
{
    Title =title;
    Language =language;
    CodecName=codecname;
    Sample_rate=sample_rate;
}
@end

//@interface Audioinfo()
//@property (readwrite,nonatomic,strong) NSString* Title;
//@property (readwrite,nonatomic,strong) NSString* Language;
//@property (readwrite,nonatomic,strong) NSString* CodecName;
//@property (readwrite,nonatomic) int sample_rate    ; //采样率
//@end
@implementation MoboThumbnail


+(MoboThumbnail *)MoboThumbnailonce{
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        if (manager == nil) {
            manager = [[MoboThumbnail alloc] init];
        }
    });
    
    return manager;
}



-(int) get_resolution:(AVFormatContext *)pFormatCtx
                      pCodecCtx: (AVCodecContext *)pCodecCtx
                      pFrame:(AVFrame *)pFrame
                      pCodec:(AVCodec *)pCodec
                      gen_time:(int)gen_second
                      videoStream:(int )videoStream
                      gen_width: (int) gen_width
                      gen_height: (int) gen_height;

{
    
    AVPicture picture;
    
    // Get a pointer to the codec context for the video stream
    pCodecCtx=pFormatCtx->streams[videoStream]->codec;
    AVStream *video_stream = pFormatCtx->streams[videoStream];
    pFormatCtx->streams[videoStream]->discard = AVDISCARD_DEFAULT; /*½ö¶ÁÈ¡ÐèÒªÁ÷ÖÐµÄ°ü*/
    
    // Find the decoder for the video stream
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec==NULL) {
        //        ffmpeg_func.  avformat_close_input(&pFormatCtx);
        return -4;
    }
    // Open codec
    if(avcodec_open2(pCodecCtx, pCodec, NULL)<0) {
        //        ffmpeg_func.  avformat_close_input(&pFormatCtx);
        return -5;
    }
    // Allocate video frame
    pFrame=av_frame_alloc();
    
    AVPacket packet;
    int frameFinished=0;
    if (gen_second == 0) {
       gen_second =(int)(pFormatCtx->duration / AV_TIME_BASE)/5;
    }
    
    int64_t seek_target = 0;
    seek_target = gen_second * AV_TIME_BASE;
    seek_target = av_rescale_q(seek_target, AV_TIME_BASE_Q, video_stream->time_base);
    avformat_seek_file(pFormatCtx, videoStream, 0, seek_target, INT64_MAX, AVSEEK_FLAG_FRAME);
    
    while(!frameFinished&&av_read_frame(pFormatCtx, &packet)>=0)
    {
        if(packet.stream_index==videoStream)
        {
                int len = avcodec_send_packet(pCodecCtx, &packet);
                
                do {
                    len  = avcodec_receive_frame(pCodecCtx, pFrame);
                    if(len == 0)
                    {
                        if (pFrame->data[0]) {
                            frameFinished =true;
                            break;
                        }
                    }
                    
                } while (len == 0);
        }
        
        
    }
    
    if (gen_width == 0 || gen_height == 0) {
                gen_width  = pCodecCtx->width;
                gen_height = pCodecCtx->height;
            }

    
        int    ret = avpicture_alloc(&picture, AV_PIX_FMT_RGB24,gen_width , gen_height);
        
            if (ret < 0) {
                if (pFrame)
                    av_frame_free(&pFrame);
                return 0;
            }
    
    struct SwsContext   *sws_context = NULL;
    
            sws_context = sws_getCachedContext(sws_context,
                                               pCodecCtx->width,
                                               pCodecCtx->height,
                                               pCodecCtx->pix_fmt,
                                               gen_width,
                                               gen_height,
                                               AV_PIX_FMT_RGB24,
                                               SWS_FAST_BILINEAR,
                                               NULL, NULL, NULL);
        
            sws_scale(sws_context,
                      (const uint8_t **)pFrame->data,
                      pFrame->linesize,
                      0,
                      pCodecCtx->height,
                      picture.data,
                      picture.linesize);
    
        NSData *rgb = [NSData dataWithBytes:picture.data[0] length:picture.linesize[0] * gen_height];
        CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)(rgb));
        if (provider) {
           CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
           if (colorSpace) {
                   CGImageRef imageRef = CGImageCreate(gen_width,
                                                       gen_height,
                                                       8,
                                                       24,
                                                       picture.linesize[0],
                                                       colorSpace,
                                                       kCGBitmapByteOrderDefault,
                                                       provider,
                                                       NULL,
                                                       YES, // NO
                                                       kCGRenderingIntentDefault);
           
                   if (imageRef) {
                       _image = [UIImage imageWithCGImage:imageRef];
                       CGImageRelease(imageRef);
                   }
                   CGColorSpaceRelease(colorSpace);
               }
               CGDataProviderRelease(provider);
           }
           
           if (!_image) {
               return 0 ;
           }

        avpicture_free(&picture);
    
        AVStream *video_st = pFormatCtx->streams[videoStream];
        float ratio = 1;
        if(pFrame->sample_aspect_ratio.num && pFrame->sample_aspect_ratio.den)
            ratio = pFrame->sample_aspect_ratio.num / pFrame->sample_aspect_ratio.den;
        if(!ratio)
            ratio = 1;
         _Video_width = video_st->codec->width * ratio;
         _Video_height = video_st->codec->height;
        int angle = 0;
        AVDictionary **meta_vs = &video_st->metadata;
        if(meta_vs){
            AVDictionaryEntry *e;
            e = av_dict_get(*meta_vs, "rotate", NULL, 0);
            if(e)
                if(e->value){
                    angle = atoi(e->value);
                }
        }
        if(angle == 90 || angle == 270){
            int tmp_width = _Video_width;
            _Video_width = _Video_height;
            _Video_height = tmp_width;
        }
        
        int bit_rate = (int)video_st->codec->bit_rate;
        if(bit_rate <= 0)
            _Video_bitrate = (int)pFormatCtx->bit_rate/1000;
        _Video_codecname =avcodec_get_name(video_st->codec->codec_id);
        avcodec_close(video_st->codec);
//    }
    av_free(pFrame);
    return 0;
}

- (int)cmp_save_video_shot:(NSString *)str gen_time:(int)gen_time gen_width:(int)gen_width gen_height:(int)gen_height
{
    NSString *filename =[NSString stringWithFormat:@"%@",str];
    BOOL isaudiofile = false;
    int ret = 0;
    AVFrame *pFrame;
    AVCodec *pCodec;
    AVCodecContext *pCodecCtx;
    AVFormatContext * fmt_ctx =NULL;
//    _isaudiofile =false;
    _Audioinfos =[NSMutableArray array];
    _subtitleinfos =[NSMutableArray array];
    /* register all formats and codecs */
    _image = NULL;
    av_register_all();
    NSString *suffix = [filename pathExtension];
    const char * Filename =[filename UTF8String];

    if([suffix isEqualToString:@"mp3"]||[suffix isEqualToString:@"wav"]||[suffix isEqualToString:@"wma"])
    {
        isaudiofile = true;
        _image = [UIImage imageNamed:@"icon_music"];
        
        if(!_image)
        {
            NSLog(@" this  image is null                     == == = == = =");
        }
    }
    //NSLog(@"file name is :%@",suffix);
    
    fmt_ctx = avformat_alloc_context();
    if (!fmt_ctx)
    {
        NSLog(@"alloc formatcontext error");
    }
    
    if (avformat_open_input(&fmt_ctx, Filename , NULL, NULL) < 0) {
         //  fprintf(stderr, "Could not open source file %@\n", filename);
            return -1;
     }
        /* retrieve stream information */
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
            avformat_close_input(&fmt_ctx);
            fprintf(stderr, "Could not find stream information\n");
            return -2;
        }
    if (!fmt_ctx->duration) {
            //如果时长为0则关闭文件
            avformat_close_input(&fmt_ctx);
            return -2;
        }
       _file_duration =(uint64_t)(fmt_ctx->duration/1000);              //定义文件时长

        char* unknown_str = "Unknown";
        char str_temp[100] = "\0";
        int videoStream = -1, i = 0, subtitle_i = 0, audio_i = 0;
        for (i = 0; i < fmt_ctx->nb_streams; i++) {
    
            fmt_ctx->streams[i]->discard = AVDISCARD_NONE;
            
             if(fmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO&&!isaudiofile)
             {
                videoStream=i;
                AVStream *video_st = fmt_ctx->streams[i];
                
                pCodec= avcodec_find_decoder(video_st->codec->codec_id);
                
                int profile = video_st->codec->profile;
                if (video_st->codec->profile > 66 && !video_st->codec->has_b_frames)
                    profile = 66;
                
                //char *videoCodecName = NULL;
                _Video_codecname = avcodec_get_name(video_st->codec->codec_id);
                if (_Video_codecname == NULL) {
                    _Video_codecname = "Unknown";
                }
                 [self get_resolution:fmt_ctx pCodecCtx: pCodecCtx pFrame:pFrame pCodec:pCodec gen_time:gen_time videoStream:videoStream gen_width: gen_width  gen_height: gen_height];
             }
             else if (fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
             {
                 audio_i++;
                 NSLog(@" the audio numers :%d",audio_i);
//                 NSLog(@" this is  audio  stream track");
                 AVStream *audio_st = fmt_ctx->streams[i];
                 //LOG("cmp_save_video_shot codec name = %x",audio_st->codec->codec->name);
                 
                 AVCodecContext *aCodec = fmt_ctx->streams[i]->codec;
                 AVDictionaryEntry *lang = NULL;
                 
                 NSString *title;
                 NSString *codecname;
                 NSString *language;
                 int rate;
                 Audioinfo *audiof =[[Audioinfo alloc]init] ;
                 lang = av_dict_get(fmt_ctx->streams[i]->metadata, "title", NULL, 0);
                 if (lang != NULL) {
                     title =  [NSString stringWithFormat:@"%s",lang->value];
                 } else {
                     snprintf(str_temp, 99, "Unknown %d", audio_i);
                     title = [NSString stringWithFormat:@"%s",str_temp] ;
                 }
                 lang = av_dict_get(fmt_ctx->streams[i]->metadata, "language", NULL, 0);
                 if (lang != NULL) {
                     language = [NSString stringWithFormat:@"%s",lang->value];
                 } else {
                     language = [NSString stringWithFormat:@"%s",unknown_str] ;
                 }
                 codecname =[NSString stringWithFormat:@"%s",avcodec_get_name(aCodec->codec_id)];
                 if (codecname == NULL) {
                     codecname = [NSString stringWithFormat:@"%s",unknown_str] ;
                 }
                 pCodec=avcodec_find_decoder(audio_st->codec->codec_id);
                 
                 rate =audio_st->codec->sample_rate;
                 _audio_channels =audio_st->codec->channels;
                 if (audio_st->codec) {
                     //Èç¹û¹Ø±ÕÒ»¸öÎ´´ò¿ªµÄaudio_st->codec£¬»áÍË³ö
                     //              LOG("before close:%x", audio_st->codec);
                     //              ffmpeg_func.  avcodec_close(audio_st->codec);
                     //              LOG("after close%x", audio_st->codec);
                     
                 }
                 [audiof setaudio:title Language:language CodecName:codecname sample_rate:rate];
                 [_Audioinfos addObject:audiof];
             } else if (fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
                 subtitle_i++;
                 AVCodecContext *sCodec = fmt_ctx->streams[i]->codec;
                 AVDictionaryEntry *lang = NULL;
                 Subtitleinfo *subtitleinfo =[[Subtitleinfo alloc]init];
                 
                 NSString *title;
                 NSString *codecname;
                 NSString *language;
                 int rate;
                 
                 
                 lang = av_dict_get(fmt_ctx->streams[i]->metadata, "title", NULL, 0);
                 if (lang != NULL) {
                     title = [NSString stringWithFormat:@"%s",lang->value];
                 } else {
                     snprintf(str_temp, 99, "Unknown %d", subtitle_i);
                     title = [NSString stringWithFormat:@"%s",str_temp]  ;
                 }
                 
                 lang = av_dict_get(fmt_ctx->streams[i]->metadata, "language", NULL, 0);
                 if (lang != NULL) {
                     language = [NSString stringWithFormat:@"%s",lang->value];
                 } else {
                     language = [NSString stringWithFormat:@"%s",unknown_str] ;
                 }
                 codecname =[NSString stringWithFormat:@"%s",avcodec_get_name(sCodec->codec_id)];
                 if (codecname == NULL) {
                     codecname = [NSString stringWithFormat:@"%s",unknown_str] ;
                 }
                 [subtitleinfo setsubtitle:title Language:language CodecName:codecname sample_rate:rate];
                 [_subtitleinfos addObject:subtitleinfo];
             }
        }

        int64_t file_size = fmt_ctx->pb ? (int64_t)avio_size(fmt_ctx->pb) : -1;
        _File_size =file_size/1024;
    NSLog(@" =!!!!!!!!!!!!!!!!!!!!!! file size %llu",_File_size);

//        if (!gen_width||!gen_height) {         //如果宽或者高为0 则返回-1
//            avformat_close_input(&fmt_ctx);
//            return -1;
//        }
    
    
    
    
        if(videoStream==-1) {
            avformat_close_input(&fmt_ctx);
            return -3;
        }
//    NSLog(@"file name is :%@",suffix);

    NSLog(@"audio infos  :  == = = == %@",_Audioinfos);
    // Get a pointer to the codec context for the video stream
        pCodecCtx=fmt_ctx->streams[videoStream]->codec;
    
        fmt_ctx->streams[videoStream]->discard = AVDISCARD_DEFAULT;
    
    // Find the decoder for the video stream
        pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
        if(pCodec==NULL)
        {
           avformat_close_input(&fmt_ctx);
            return -4;
        }

    
        // Open codec
        if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
        {
            avformat_close_input(&fmt_ctx);
            return -5;
        }
    
        ret = 0;
        avcodec_close(pCodecCtx);
        avformat_close_input(&fmt_ctx);
    
    return ret;
}



@end
